/*!
 * rollup-plugin-2webp v2.0.1
 * (c) Mon Aug 26 2024 16:03:33 GMT+0800 (中国标准时间) liyuzhong
 * Released under the ISC License.
 */
'use strict';

var path = require('path');
var fs = require('fs');
var process = require('child_process');
var url = require('url');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var process__default = /*#__PURE__*/_interopDefaultLegacy(process);

/**
 * 匹配图片文件类型
 * @param {*} path 
 * @param {*} imageTypes 
 * @returns 
 */
const matchFileType = (filepath, imageTypes) => {

    if (!imageTypes) { return false; }

    const arr = filepath.split('.');
    const suffix = arr[arr.length - 1];
    return imageTypes.includes(`.${suffix}`);
};

function genVarByFileName(fileName, type){
    if(!fileName) return '';
    const fileSplit = fileName.split(".");
    let pureFileName = fileSplit.slice(0, fileSplit.length - 1).join(".");
    const joinDot = type === 'css'? '-':'_';
    pureFileName = pureFileName.replace(/[^a-z0-9]/gi, joinDot);
    return type === 'css' ? `--${pureFileName}` : `WEBP_${pureFileName}`.toUpperCase()
}

function genFileCssVar(fileName){
    return genVarByFileName(fileName, 'css')
}

function genFileJsVar(fileName){
    return genVarByFileName(fileName, 'js')
}

function toArray(b){
    return toString.apply(b) === '[object Array]'? b: b ? [b] : [];
}

const __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('rollup-plugin-2webp.js', document.baseURI).href)));
const __dirname$1 = path__default["default"].dirname(__filename$1);

// 获取插件根目录
const pluginDir = path.resolve(__dirname$1, "..");
// 插件内的webp命令工具
const command = path.join(pluginDir, 'libwebp', 'bin', 'cwebp.exe');

let webpInfo$1 = {};

function isDirectory(abs){
    return fs__default["default"].statSync(abs).isDirectory();
}

function isExclude(abs, exclude, cwd){
    exclude = toArray(exclude);
    if(exclude.length){
        for(let i = 0; i < exclude.length; i++){
            const excludePath = path__default["default"].join(cwd, exclude[i]);
            if(abs.indexOf(excludePath) === 0){
                return true
            }
        }        
    }
    return false
}

const imageToWebp = function (filePath, quality) {
    let nPath = `${filePath}.webp`;
    
    // 读取原文件的信息
    const fileName = path__default["default"].basename(filePath);
    const sImgStat = fs__default["default"].statSync(filePath);

    if(fs__default["default"].existsSync(nPath)){
        // 如果对应的webp文件已经存在，比较webp文件与原图片的更新时间，webp文件的更新时间晚于原图片文件的更新时间则直接读取文件内容返回
        const stat = fs__default["default"].statSync(nPath);

        const webpMTime = stat.mtime.getTime();
        const sImgMTime = sImgStat.mtime.getTime();

        if(webpMTime > sImgMTime){
            let imageData = fs__default["default"].readFileSync(nPath);     
            if(stat.size > sImgStat.size){
                return { fileName: fileName, filepath:nPath, file: imageData, isMin: false}
            }

            return { fileName: fileName, filepath:nPath, file: imageData, isMin: true }
        }
    }

    // 重新转成新的webp文件
    const execCommand = `${command} -q ${quality} ${filePath} -o ${nPath}`;

    process__default["default"].execSync(execCommand);
    if(fs__default["default"].existsSync(nPath)){
        const stat = fs__default["default"].statSync(nPath);
        const imageData = fs__default["default"].readFileSync(nPath);
        return { fileName: fileName, filepath:nPath, file: imageData, isMin: (stat.size < sImgStat.size)}
    }else {
        return null
    }      
};

const scanAssets =  function (config){
    const { assets, exclude, imageTypes, quality, cwd } = config;

    console.info(`scanAssets ${imageTypes} in ${assets}...`);
    if (fs__default["default"].existsSync(assets) === false) {
        return;
    }

    const files = fs__default["default"].readdirSync(assets);
    files.forEach((v) => {

        const abs = path__default["default"].join(assets, v);
        const iexl = isExclude(abs, exclude, cwd);

        if (isDirectory(abs) && !iexl) {
            const c = JSON.parse(JSON.stringify(config));
            c.assets = abs;
            scanAssets(c);
        } else if (matchFileType(abs, imageTypes)) {
            const res = imageToWebp(abs, quality);
            if(res){
                webpInfo$1[res.fileName] = res;
                console.info(`convert file ${abs} to webp success...`);
            }
        }
    });

    return webpInfo$1
};

// 插件默认配置
const DEFAULT_OPTIONS = {
    // 默认需要转webp的图片格式
    imageTypes: ['.png', '.jpg', 'jpeg'],
    // 图片资源所在目录
    assets: 'src/assets',
    // 排除目录
    exclude: '',
    // 转成webp的质量系数
    quality: 80,
    // css引用处理方案，false-不处理， true/function-将根据是否支持webp图片为图片定义css变量，并将css文件中的图片地址替换成对应的css变量
    // genCssVar(cssObject, webpCssObject)
    genCssVar: true,
    // js引用处理方案，false-不处理， true/function-将根据是否支持webp图片为图片定义js变量，并将js文件中的图片地址替换成对应的js变量
    // genJsVar(jsObject, webpjsObject)
    genJsVar: true,
    injectPos: '<body>',
    // 主动处理插入脚本
    // 接口定义 inject(html, {cssObject:cssObject, webpCssObject: webpCssObject, jsObject: jsObject, webpJsObject: webpJsObject})
    inject: '',
    baseUrl: '',
    // 是否使用同步模式校验webp支持特性，true - 同步模式，false - 异步
    syncCheckWebpSupport: true,
};

// CSS变量定义对象
// css图片引用示例"/h5open3-jd/dist/assets/video-tip-icon@2x.69ff60ad.png"
let cssObject = {};
// webp js对象，存放转换后图片文件路径信息
let jsObject = {};
// 针对webp环境的css变量定义
let webpCssObject = {};
// 针对webp环境的js变量定义
let webpJsObject = {};

let webpInfo = {};

function towebp(custOptions) {

    custOptions = custOptions ? custOptions : {};

    // 合并的配置
    const config = {
        ...DEFAULT_OPTIONS,
        ...custOptions
    };

    // 6位随机串，用于注入脚本变量定义
    const timeStr = new Date().getTime() + '';
    const rStr = timeStr.substring(timeStr.length - 6);

    const cwd = path__default["default"].resolve("./");
    // 先根据配置的图片目录将目录下所有的图片转成webp，并返回对应的文件名与webp文件的键值对
    webpInfo = scanAssets({assets: path__default["default"].join(cwd, config.assets), exclude: config.exclude, imageTypes:config.imageTypes, quality: config.quality, cwd: cwd});

    return {
        name: 'vite-plugin-webp',
        configResolved(resolvedConfig) {
            // 存储最终解析的配置
            config.alias = resolvedConfig?.resolve?.alias || [];
            config.baseUrl = resolvedConfig.env.BASE_URL;
        },
        transformIndexHtml(html) {
            // 增加webp支持判断同时增加对应的标记属性

            if (typeof config.inject === 'function') {
                // 支持主动处理插入脚本
                return config.inject(html, { cssObject: cssObject, webpCssObject: webpCssObject, jsObject: jsObject, webpJsObject: webpJsObject })
            }

            const realTimeSupportBody = `
            var result = document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0;
            localStorage.SUPPORT_WEBP = result? 1:2;
            callback(result)
            `;

            const asyncSupportBody = `
            var img = new Image();
            img.onload = function(){
                var result = (img.width > 0) && (img.height > 0);
                localStorage.SUPPORT_WEBP = result? 1:2;
                callback(result)
            };
            img.onerror = function(){
                localStorage.SUPPORT_WEBP = 2
                callback(false);
            };
            img.src = '';
            `;

            let injectScript = `
            <script type="text/javascript">
            function isSupportWebp${rStr}(callback) {
                try{
                    if(localStorage.SUPPORT_WEBP == 1) return callback(true);
                    if(localStorage.SUPPORT_WEBP == 2) return callback(false);
                    ${config.syncCheckWebpSupport? realTimeSupportBody: asyncSupportBody}
                }catch(e){
                    callback(false)
                }                
            }
            isSupportWebp${rStr}(function(r){
                if(r) {
                    document.querySelector("body").setAttribute("data-webp",1);
                }
            })
            </script>
            `;

            if (config.genCssVar) {

                if (typeof config.genCssVar === 'function') {
                    injectScript += config.genCssVar(cssObject, webpCssObject);
                } else {
                    const cssObjectKeys = Object.keys(cssObject);
                    const webpCssObjectKeys = Object.keys(webpCssObject);
                    injectScript += `
                    <style type="text/css">
                        :root{
                            ${cssObjectKeys.map((item) => {
                        return `${item}:url("${cssObject[item]}");`
                    }).join("")
                        }
                        }
                        body[data-webp]{
                            ${webpCssObjectKeys.map((item) => {
                            return `${item}:url("${webpCssObject[item]}");`
                        }).join("")
                        }
                        }
                    </style>
                    `;
                }
            }

            if (config.genJsVar) {
                if (typeof config.genJsVar === 'function') {
                    injectScript += config.genJsVar(jsObject, webpJsObject);
                } else {
                    const jsObjectKeys = Object.keys(jsObject);
                    injectScript += `
                    <script type="text/javascript">
                    isSupportWebp${rStr}(function(r){
                        window.webp_${rStr} = {
                            ${jsObjectKeys.map((item) => {
                                return `${item}:r? "${webpJsObject[item]}":"${jsObject[item]}",`
                            }).join("")}
                        }
                    })                    
                    </script>
                    `;
                }
            }

            config.injectPos = config.injectPos && typeof config.injectPos === 'string' ? config.injectPos : '<body>';

            return html.replace(config.injectPos, `${config.injectPos} ${injectScript}`)

        },
        async generateBundle(options, bundle) {
            // 包含所有资源文件

            const keys = Object.keys(bundle);

            for (let i = 0; i < keys.length; i++) {

                const fileName = keys[i];
                const b = bundle[fileName];
                const realSourceFileName = path__default["default"].basename(b.name);
                
                if (webpInfo[realSourceFileName]) {

                    const webpFileInfo = webpInfo[realSourceFileName];
                    const webpIsMin = webpFileInfo.isMin;
                    const webpObj = {
                        fileName: `${b.fileName}.webp`,
                        name: `${b.name}.webp`,
                        type: b.type,
                        source: webpFileInfo.file
                    };

                    let cssVar = '';
                    let jsVar = '';

                    if (config.genCssVar) {
                        cssVar = genFileCssVar(b.fileName);
                        if (cssVar) {
                            cssObject[cssVar] = `${config.baseUrl}${b.fileName}`;
                            webpCssObject[cssVar] = `${config.baseUrl}${webpIsMin? webpObj.fileName: b.fileName}`;
                        }
                    }

                    if (config.genJsVar) {
                        jsVar = genFileJsVar(b.fileName);
                        if (jsVar) {
                            jsObject[jsVar] = `${config.baseUrl}${b.fileName}`;
                            webpJsObject[jsVar] = `${config.baseUrl}${webpIsMin? webpObj.fileName: b.fileName}`;
                        }
                    }

                    this.emitFile(webpObj);
                }
            }

            // 替换css和js文件中的图片引用
            for (let i = 0; i < keys.length; i++) {
                const fileName = keys[i];
                if (config.genCssVar && matchFileType(fileName, ['.css'])) {
                    console.info(`replace css var in file ${fileName}`);
                    // 将css的对应图片引用换成css变量
                    const cssObjectKeys = Object.keys(cssObject);
                    cssObjectKeys.forEach(item => {
                        const fileUrl = cssObject[item];
                        const source = bundle[fileName].source;
                        if (~source.indexOf(`url("${fileUrl}")`) && webpCssObject[item]) {
                            bundle[fileName].source = source.replaceAll(`url("${fileUrl}")`, `var(${item})`);
                        }
                    });
                }

                if (config.genJsVar && matchFileType(fileName, ['.js'])) {
                    console.info(`replace js var in file ${fileName}`);
                    // 将css的对应图片引用换成css变量
                    const jsObjectKeys = Object.keys(jsObject);
                    jsObjectKeys.forEach(item => {
                        const fileUrl = jsObject[item];
                        const code = bundle[fileName].code;
                        if (~code.indexOf(`"${fileUrl}"`) && webpJsObject[item]) {
                            bundle[fileName].code = bundle[fileName].code.replaceAll(`"${fileUrl}"`, `window.webp_${rStr}.${item}`);
                        }
                    });
                }
            }

            return bundle
        }
    }
}

module.exports = towebp;
